package com.doudou.pay.helper.server.handler;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.doudou.pay.helper.entities.ResponsePay;
import com.doudou.pay.helper.server.protoco.ProtocoConsts;
import com.doudou.pay.helper.server.protoco.TcpMsg;
import com.doudou.pay.helper.server.protoco.TcpMsgBody;
import com.doudou.pay.helper.server.protoco.TcpMsgHeader;
import com.doudou.pay.helper.service.AuthTokenService;
import com.doudou.pay.helper.service.PayService;

import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

public class PayHelperChannelHandler extends ChannelInboundHandlerAdapter {
	
	private Logger logger = Logger.getLogger(this.getClass());
	
	private AuthTokenService authTokenService;
	private PayService payService;
	
	public PayHelperChannelHandler(AuthTokenService authTokenService, PayService payService) {
		super();
		this.authTokenService = authTokenService;
		this.payService = payService;
	}

	@Override
	public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
		super.channelRegistered(ctx);
		logger.info("channelRegistered => " + ctx.name());
	}

	@Override
	public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
		super.channelUnregistered(ctx);
		ChannelCacheManager.remove(ctx);
		logger.info("channelUnregistered => " + ctx.name());
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		super.channelActive(ctx);
		logger.info("channelActive => " + ctx.name());
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		super.channelInactive(ctx);
		logger.info("channelInactive => " + ctx.name());
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		logger.info("channelRead => " + ctx.name());
		try {

//			ByteBuf byteBuf = (ByteBuf) msg;
			JSONObject jsonObject = JSON.parseObject(String.valueOf(msg));
			
			String channelId = ctx.channel().id().asLongText();
			
			TcpMsgHeader tcpMsgHeader = jsonObject.getObject("msgHeader", TcpMsgHeader.class);
			switch (tcpMsgHeader.getMsgType()) {
			case ProtocoConsts.TCP_MSG_TYPE_AUTHENTICATION:{
				TcpMsgBody tcpMsgBody = new TcpMsgBody(ProtocoConsts.FAIL,ProtocoConsts.FAIL_DEFINE);
				//是否是有效的token
				if(authTokenService.isValidAuthToken(tcpMsgHeader.getAuthToken())) {
					
					String oldChannelId = authTokenService.getChannelId(tcpMsgHeader.getAuthToken());
					if(StringUtils.isNotEmpty(oldChannelId)) {
						//如果存在chnnelId,说明设备在别的机器登录
						Channel oldChannel = ChannelCacheManager.getChannel(tcpMsgHeader.getAuthToken());
						if(null != oldChannel) {
							oldChannel.close();
							oldChannel.disconnect();
						}
					}
					
					if(authTokenService.authentication(tcpMsgHeader.getAuthToken(), channelId)) {
						tcpMsgBody.setCode(ProtocoConsts.SUCCESS);
						tcpMsgBody.setDefine(ProtocoConsts.SUCCESS_DEFINE);
						
						ChannelCacheManager.put(tcpMsgHeader.getAuthToken(), ctx);
					}
				}
				TcpMsg tcpMsg = new TcpMsg(tcpMsgHeader, tcpMsgBody);
				ctx.writeAndFlush(Unpooled.copiedBuffer(WriteMsgHandler.msgEncode(tcpMsg)));
				break;
			}
				
			case ProtocoConsts.TCP_MSG_TYPE_HEARTBEAT:{
				if(authTokenService.isAuthtication(tcpMsgHeader.getAuthToken(), channelId)) {
					ChannelCacheManager.put(tcpMsgHeader.getAuthToken(), ctx);
					TcpMsg tcpMsg = new TcpMsg(tcpMsgHeader, null);
					ctx.writeAndFlush(Unpooled.copiedBuffer(WriteMsgHandler.msgEncode(tcpMsg)));
				}
				break;
			}
				
			case ProtocoConsts.TCP_MSG_TYPE_RESPONSE_PAY:{
				if(authTokenService.isAuthtication(tcpMsgHeader.getAuthToken(), channelId)) {
					ChannelCacheManager.put(tcpMsgHeader.getAuthToken(), ctx);
					
					JSONObject msgBodyJson = jsonObject.getJSONObject("msgBody");
					System.out.println("支付响应:" + msgBodyJson);
					ResponsePay responsePay = msgBodyJson.getObject("data", ResponsePay.class);
					
					responsePay.setAuthToken(tcpMsgHeader.getAuthToken());
					responsePay.setCode(msgBodyJson.getIntValue("code"));
					payService.responseHttpPay(responsePay);
//					payService.responsePay(msgBodyJson.getIntValue("code"),responsePay);
				}
				break;
			}
			default:
				break;
			}

		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			ReferenceCountUtil.release(msg);
		}
	}

//	private byte[] getBytes(ByteBuf msg) {
//
//		if (!msg.hasArray()) {
//			int length = msg.readableBytes();
//			byte[] bytes = new byte[length];
//			msg.getBytes(0, bytes);
//			return bytes;
//		}
//		return msg.array();
//	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		/**
		 * exceptionCaught() 事件处理方法是当出现 Throwable 对象才会被调用，即当 Netty 由于 IO
		 * 错误或者处理器在处理事件时抛出的异常时。在大部分情况下，捕获的异常应该被记录下来 并且把关联的 channel
		 * 给关闭掉。然而这个方法的处理方式会在遇到不同异常的情况下有不 同的实现，比如你可能想在关闭连接之前发送一个错误码的响应消息。
		 */
		// 出现异常就关闭
		authTokenService.deleteAuthentication(ChannelCacheManager.getAuthToken(ctx));
		ChannelCacheManager.remove(ctx);
		cause.printStackTrace();
		ctx.close();
	}
}
